//===--- Mutex.h - Mutex, ConditionVariable, & ReadWriteLock ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Mutex, ConditionVariable, Read/Write lock, and Scoped lock abstractions
// for use in Swift runtime.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_RUNTIME_MUTEX_H
#define SWIFT_RUNTIME_MUTEX_H

#include <type_traits>

#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__))
#include "swift/Runtime/MutexPThread.h"
#elif defined(_MSC_VER)
#include "swift/Runtime/MutexWin32.h"
#else
#error "Implement equvalent of MutexPThread.h/cpp for your platform."
#endif

namespace swift {

/// A ConditionVariable that works with Mutex to allow -- as an example --
/// multi-threaded producers and consumers to signal each other in a safe way.
class ConditionVariable {
  friend class Mutex;

  ConditionVariable(const ConditionVariable &) = delete;
  ConditionVariable &operator=(const ConditionVariable &) = delete;
  ConditionVariable(ConditionVariable &&) = delete;
  ConditionVariable &operator=(ConditionVariable &&) = delete;

public:
  ConditionVariable() { ConditionPlatformHelper::init(Handle); }
  ~ConditionVariable() { ConditionPlatformHelper::destroy(Handle); }

  /// Notifies one waiter (if any exists) that the condition has been met.
  ///
  /// Note: To avoid missed notification it is best hold the related mutex
  //        lock when calling notifyOne.
  void notifyOne() { ConditionPlatformHelper::notifyOne(Handle); }

  /// Notifies all waiters (if any exists) that the condition has been met.
  ///
  /// Note: To avoid missed notification it is best hold the related mutex
  //        lock when calling notifyAll.
  void notifyAll() { ConditionPlatformHelper::notifyAll(Handle); }

private:
  ConditionHandle Handle;
};

/// A Mutex object that supports `BasicLockable` and `Lockable` C++ concepts.
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
/// See http://en.cppreference.com/w/cpp/concept/Lockable
///
/// This is NOT a recursive mutex.
class Mutex {

  Mutex(const Mutex &) = delete;
  Mutex &operator=(const Mutex &) = delete;
  Mutex(Mutex &&) = delete;
  Mutex &operator=(Mutex &&) = delete;

public:
  /// Constructs a non-recursive mutex.
  ///
  /// If `checked` is true the mutex will attempt to check for misuse and
  /// fatalError when detected. If `checked` is false (the default) the
  /// mutex will make little to no effort to check for misuse (more efficient).
  explicit Mutex(bool checked = false) {
    MutexPlatformHelper::init(Handle, checked);
  }
  ~Mutex() { MutexPlatformHelper::destroy(Handle); }

  /// The lock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread until exclusive ownership of the mutex
  ///   can be obtained.
  /// - Prior m.unlock() operations on the same mutex synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the mutex (likely a deadlock).
  /// - Does not throw exceptions but will halt on error (fatalError).
  void lock() { MutexPlatformHelper::lock(Handle); }

  /// The unlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the mutex and
  ///   synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the mutex.
  /// - Does not throw exceptions but will halt on error (fatalError).
  void unlock() { MutexPlatformHelper::unlock(Handle); }

  /// The try_lock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Attempts to obtain exclusive ownership of the mutex for the calling
  ///   thread without blocking. If ownership is not obtained, returns
  ///   immediately. The function is allowed to spuriously fail and return
  ///   even if the mutex is not currently owned by another thread.
  /// - If try_lock() succeeds, prior unlock() operations on the same object
  ///   synchronize-with this operation. lock() does not synchronize with a
  ///   failed try_lock()
  /// - The behavior is undefined if the calling thread already owns
  ///   the mutex (likely a deadlock)?
  /// - Does not throw exceptions but will halt on error (fatalError).
  bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }

  /// Releases lock, waits on supplied condition, and relocks before returning.
  ///
  /// Precondition: Mutex held by this thread, undefined otherwise.
  void wait(ConditionVariable &condition) {
    ConditionPlatformHelper::wait(condition.Handle, Handle);
  }

  /// Acquires lock before calling the supplied critical section and releases
  /// lock on return from critical section.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following mutates value while holding the mutex lock.
  ///
  /// ```
  ///   mutex.lock([&value] { value++; });
  /// ```
  ///
  /// Precondition: Mutex not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  void lock(CriticalSection criticalSection) {
    lock();
    criticalSection();
    unlock();
  }

  /// Acquires lock before calling the supplied critical section. If critical
  /// section returns `false` then it will wait on the supplied condition and
  /// call the critical section again when wait returns (after acquiring lock).
  /// If critical section returns `true` (done) it will no longer wait, it
  /// will release the lock and return (lockOrWait returns to caller).
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following will loop waiting on the condition until
  /// `value > 0`. It will then "consume" that value and stop looping.
  /// ...all while being correctly protected by mutex.
  ///
  /// ```
  ///   mutex.lockOrWait(condition, [&value] {
  ///     if (value > 0) {
  ///       value--;
  ///       return true;
  ///     }
  ///    return false;
  ///   });
  /// ```
  ///
  /// Precondition: Mutex not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  void lockOrWait(ConditionVariable &condition,
                  CriticalSection criticalSection) {
    lock([&] {
      while (!criticalSection()) {
        wait(condition);
      }
    });
  }

  /// Acquires lock before calling the supplied critical section and on return
  /// from critical section it notifies one waiter of supplied condition and
  /// then releases the lock.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following mutates value while holding the mutex lock and
  /// then notifies one condition waiter about this change.
  ///
  /// ```
  ///   mutex.lockAndNotifyOne([&value] { value++; });
  /// ```
  ///
  /// Precondition: Mutex not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  void lockAndNotifyOne(ConditionVariable &condition,
                        CriticalSection criticalSection) {
    lock([&] {
      criticalSection();
      condition.notifyOne();
    });
  }

  /// Acquires lock before calling the supplied critical section and on return
  /// from critical section it notifies all waiters of supplied condition and
  /// then releases the lock.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following mutates value while holding the mutex lock and
  /// then notifies all condition waiters about this change.
  ///
  /// ```
  ///   mutex.lockAndNotifyAll([&value] { value++; });
  /// ```
  ///
  /// Precondition: Mutex not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  void lockAndNotifyAll(ConditionVariable &condition,
                        CriticalSection criticalSection) {
    lock([&] {
      criticalSection();
      condition.notifyAll();
    });
  }

private:
  MutexHandle Handle;
};

/// A Read / Write lock object that has semantics similar to `BasicLockable`
/// and `Lockable` C++ concepts however it supports multiple concurrent
/// threads holding the reader lock as long as the write lock isn't held and
/// only one thread can hold the write local at the same time.
///
/// If you need static allocated ReadWriteLock use StaticReadWriteLock.
///
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
/// See http://en.cppreference.com/w/cpp/concept/Lockable
///
/// This is NOT a recursive mutex.
class ReadWriteLock {

  ReadWriteLock(const ReadWriteLock &) = delete;
  ReadWriteLock &operator=(const ReadWriteLock &) = delete;
  ReadWriteLock(ReadWriteLock &&) = delete;
  ReadWriteLock &operator=(ReadWriteLock &&) = delete;

public:
  ReadWriteLock() { ReadWriteLockPlatformHelper::init(Handle); }
  ~ReadWriteLock() { ReadWriteLockPlatformHelper::destroy(Handle); }

  /// The readLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread while the write lock is held by another 
  ///   thread and once the read lock is acquired by the calling thread
  ///   other threads are prevented from acquiring the write lock.
  /// - Multiple threads can hold the read lock at the same time.
  /// - Prior unlock() operations on the same lock synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock).
  /// - Does not throw exceptions but will halt on error (fatalError).
  ///
  /// Callers must not mutate the data protected by the ReadWriteLock while
  /// holding the read lock, the write lock must be used.
  void readLock() { ReadWriteLockPlatformHelper::readLock(Handle); }

  /// The try_readLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Attempts to obtain the read lock without blocking the calling thread.
  ///   If ownership is not obtained, returns immediately. The function is
  ///   allowed to spuriously fail and return even if the lock is not
  ///   currently owned by another thread.
  /// - If try_readLock() succeeds, prior unlock() operations on the same
  ///   object synchronize-with this operation. unlock() does not synchronize
  ///   with a failed try_readLock().
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock)?
  /// - Does not throw exceptions but will halt on error (fatalError).
  ///
  /// Callers must not mutate the data protected by the ReadWriteLock while
  /// holding the read lock, the write lock must be used.
  bool try_readLock() {
    return ReadWriteLockPlatformHelper::try_readLock(Handle);
  }

  /// The readUnlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the read lock
  ///   and synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the read lock.
  /// - Does not throw exceptions but will halt on error (fatalError).
  void readUnlock() { ReadWriteLockPlatformHelper::readUnlock(Handle); }

  /// The writeLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread while the write lock or a read lock is held
  ///   by another thread and once the write lock is acquired by the calling
  ///   thread other threads are prevented from acquiring the write lock or a
  ///   read lock.
  /// - Only one thread can hold the write lock at the same time.
  /// - Prior unlock() operations on the same lock synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock).
  /// - Does not throw exceptions but will halt on error (fatalError).
  void writeLock() { ReadWriteLockPlatformHelper::writeLock(Handle); }

  /// The try_writeLock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Attempts to obtain the write lock without blocking the calling thread.
  ///   If ownership is not obtained, returns immediately. The function is
  ///   allowed to spuriously fail and return even if the lock is not
  ///   currently owned by another thread.
  /// - If try_writeLock() succeeds, prior unlock() operations on the same
  ///   object synchronize-with this operation. unlock() does not synchronize
  ///   with a failed try_writeLock().
  /// - The behavior is undefined if the calling thread already owns
  ///   the read or write lock (likely a deadlock)?
  /// - Does not throw exceptions but will halt on error (fatalError).
  bool try_writeLock() {
    return ReadWriteLockPlatformHelper::try_writeLock(Handle);
  }

  /// The writeUnlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the write lock
  ///   and synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the write lock.
  /// - Does not throw exceptions but will halt on error (fatalError).
  void writeUnlock() { ReadWriteLockPlatformHelper::writeUnlock(Handle); }

  /// Acquires read lock before calling the supplied critical section and
  /// releases lock on return from critical section. Callers must not mutate
  /// the data protected by the ReadWriteLock while holding the read lock, the
  /// write lock must be used.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following reads the cached value while holding
  /// the read lock.
  ///
  /// ```
  ///   rw.readLock([&value] { value = cachedValue; });
  /// ```
  ///
  /// Precondition: ReadWriteLock not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  void readLock(CriticalSection criticalSection) {
    readLock();
    criticalSection();
    readUnlock();
  }

  /// Acquires write lock before calling the supplied critical section and
  /// releases lock on return from critical section.
  ///
  /// This call can block while waiting for the lock to become available.
  ///
  /// For example the following updates the cached value while holding
  /// the write lock.
  ///
  /// ```
  ///   rw.writeLock([&newValue] { cachedValue = newValue });
  /// ```
  ///
  /// Precondition: ReadWriteLock not held by this thread, undefined otherwise.
  template <typename CriticalSection>
  void writeLock(CriticalSection criticalSection) {
    writeLock();
    criticalSection();
    writeUnlock();
  }

  /// Acquires read lock before calling the read critical section and if
  /// the critical section returns `true` (done) readWriteLock returns to the
  /// caller. If the read critical section returns `false` the read lock
  /// will be dropped, the write lock will be acquired and then the read
  /// critical section will be called again. Then if the critical section
  /// returns `true` (done) readWriteLock returns to the caller. If the read
  /// critical section returns `false` then the write critical section will
  /// be called (with write lock still held).
  ///
  /// Note in all situations on return to caller the ReadWriteLock will
  /// not be held.
  ///
  /// For example the following attempts to get a cachedValue if it exists
  /// if not found it will create the needed cachedValue.
  ///
  /// ```
  ///   rw.readWriteLock(
  ///     [&value] {
  ///       if (cachedValue) {
  ///         value = cachedValue;
  ///         return true;
  ///       }
  ///       return false;
  ///     },
  ///     [&value] {
  ///       cachedValue = createValue();
  ///       value = cachedValue;
  ///     });
  /// ```
  ///
  /// Precondition: ReadWriteLock not held by this thread, undefined otherwise.
  template <typename ReadonlySection, typename WriteSection>
  void readWriteLock(ReadonlySection readSection, WriteSection writeSection) {
    bool done;
    readLock([&done, &readSection] { done = readSection(); });
    if (!done) {
      writeLock([&readSection, &writeSection] {
        if (!readSection()) {
          writeSection();
        }
      });
    }
  }

private:
  ReadWriteLockHandle Handle;
};

/// A static allocation variant of ConditionVariable.
///
/// Use ConditionVariable instead unless you need static allocation.
class StaticConditionVariable {
  friend class StaticMutex;

  StaticConditionVariable(const StaticConditionVariable &) = delete;
  StaticConditionVariable &operator=(const StaticConditionVariable &) = delete;
  StaticConditionVariable(StaticConditionVariable &&) = delete;
  StaticConditionVariable &operator=(StaticConditionVariable &&) = delete;

public:
  StaticConditionVariable()
      : Handle(ConditionPlatformHelper::staticInit()) {}

  /// See ConditionVariable::notifyOne
  void notifyOne() { ConditionPlatformHelper::notifyOne(Handle); }

  /// See ConditionVariable::notifyAll
  void notifyAll() { ConditionPlatformHelper::notifyAll(Handle); }

private:
  ConditionHandle Handle;
};

/// A static allocation variant of Mutex.
///
/// Use Mutex instead unless you need static allocation.
class StaticMutex {

  StaticMutex(const StaticMutex &) = delete;
  StaticMutex &operator=(const StaticMutex &) = delete;
  StaticMutex(StaticMutex &&) = delete;
  StaticMutex &operator=(StaticMutex &&) = delete;

public:
  StaticMutex() : Handle(MutexPlatformHelper::staticInit()) {}

  /// See Mutex::lock
  void lock() { MutexPlatformHelper::lock(Handle); }

  /// See Mutex::unlock
  void unlock() { MutexPlatformHelper::unlock(Handle); }

  /// See Mutex::try_lock
  bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }

  /// See Mutex::wait
  void wait(StaticConditionVariable &condition) {
    ConditionPlatformHelper::wait(condition.Handle, Handle);
  }

  /// See Mutex::lock
  template <typename CriticalSection>
  void lock(CriticalSection criticalSection) {
    lock();
    criticalSection();
    unlock();
  }

  /// See Mutex::lockOrWait
  template <typename CriticalSection>
  void lockOrWait(StaticConditionVariable &condition,
                  CriticalSection criticalSection) {
    lock([&] {
      while (!criticalSection()) {
        wait(condition);
      }
    });
  }

  /// See Mutex::lockAndNotifyOne
  template <typename CriticalSection>
  void lockAndNotifyOne(StaticConditionVariable &condition,
                        CriticalSection criticalSection) {
    lock([&] {
      criticalSection();
      condition.notifyOne();
    });
  }

  /// See Mutex::lockAndNotifyAll
  template <typename CriticalSection>
  void lockAndNotifyAll(StaticConditionVariable &condition,
                        CriticalSection criticalSection) {
    lock([&] {
      criticalSection();
      condition.notifyAll();
    });
  }

private:
  MutexHandle Handle;
};

/// A static allocation variant of ReadWriteLock.
///
/// Use ReadWriteLock instead unless you need static allocation.
class StaticReadWriteLock {

  StaticReadWriteLock(const StaticReadWriteLock &) = delete;
  StaticReadWriteLock &operator=(const StaticReadWriteLock &) = delete;
  StaticReadWriteLock(StaticReadWriteLock &&) = delete;
  StaticReadWriteLock &operator=(StaticReadWriteLock &&) = delete;

public:
  StaticReadWriteLock()
      : Handle(ReadWriteLockPlatformHelper::staticInit()) {}

  /// See ReadWriteLock::readLock
  void readLock() { ReadWriteLockPlatformHelper::readLock(Handle); }

  /// See ReadWriteLock::try_readLock
  bool try_readLock() {
    return ReadWriteLockPlatformHelper::try_readLock(Handle);
  }

  /// See ReadWriteLock::readUnlock
  void readUnlock() { ReadWriteLockPlatformHelper::readUnlock(Handle); }

  /// See ReadWriteLock::writeLock
  void writeLock() { ReadWriteLockPlatformHelper::writeLock(Handle); }

  /// See ReadWriteLock::try_writeLock
  bool try_writeLock() {
    return ReadWriteLockPlatformHelper::try_writeLock(Handle);
  }

  /// See ReadWriteLock::writeUnlock
  void writeUnlock() { ReadWriteLockPlatformHelper::writeUnlock(Handle); }

  /// See ReadWriteLock::readLock
  template <typename CriticalSection>
  void readLock(CriticalSection criticalSection) {
    readLock();
    criticalSection();
    readUnlock();
  }

  /// See ReadWriteLock::writeLock
  template <typename CriticalSection>
  void writeLock(CriticalSection criticalSection) {
    writeLock();
    criticalSection();
    writeUnlock();
  }

  /// See ReadWriteLock::readWriteLock
  template <typename ReadonlySection, typename WriteSection>
  void readWriteLock(ReadonlySection readSection, WriteSection writeSection) {
    bool done;
    readLock([&done, &readSection] { done = readSection(); });
    if (!done) {
      writeLock([&readSection, &writeSection] {
        if (!readSection()) {
          writeSection();
        }
      });
    }
  }

private:
  ReadWriteLockHandle Handle;
};

/// A Mutex object that supports `BasicLockable` C++ concepts. It is
/// considered
/// unsafe to use because it doesn't do any error checking. It is only for
/// use in pathways that deal with reporting fatalErrors to avoid the
/// potential
/// for recursive fatalErrors that could happen if you used Mutex.
///
/// Always use Mutex, unless in the above mentioned error pathway situation.
class StaticUnsafeMutex {

  StaticUnsafeMutex(const StaticUnsafeMutex &) = delete;
  StaticUnsafeMutex &operator=(const StaticUnsafeMutex &) = delete;
  StaticUnsafeMutex(StaticUnsafeMutex &&) = delete;
  StaticUnsafeMutex &operator=(StaticUnsafeMutex &&) = delete;

public:
  StaticUnsafeMutex() : Handle(MutexPlatformHelper::staticInit()) {}

  /// The lock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Blocks the calling thread until exclusive ownership of the mutex
  ///   can be obtained.
  /// - Prior m.unlock() operations on the same mutex synchronize-with
  ///   this lock operation.
  /// - The behavior is undefined if the calling thread already owns
  ///   the mutex (likely a deadlock).
  /// - Ignores errors that may happen, undefined when an error happens.
  void lock() { MutexPlatformHelper::unsafeLock(Handle); }

  /// The unlock() method has the following properties:
  /// - Behaves as an atomic operation.
  /// - Releases the calling thread's ownership of the mutex and
  ///   synchronizes-with the subsequent successful lock operations on
  ///   the same object.
  /// - The behavior is undefined if the calling thread does not own
  ///   the mutex.
  /// - Ignores errors that may happen, undefined when an error happens.
  void unlock() { MutexPlatformHelper::unsafeUnlock(Handle); }

private:
  MutexHandle Handle;
};

/// Compile time adjusted stack based object that locks/unlocks the supplied
/// Mutex type. Use the provided typedefs instead of this directly.
template <typename T, bool Inverted> class ScopedLockT {

  ScopedLockT() = delete;
  ScopedLockT(const ScopedLockT &) = delete;
  ScopedLockT &operator=(const ScopedLockT &) = delete;
  ScopedLockT(ScopedLockT &&) = delete;
  ScopedLockT &operator=(ScopedLockT &&) = delete;

public:
  explicit ScopedLockT(T &l) : Lock(l) {
    if (Inverted) {
      Lock.unlock();
    } else {
      Lock.lock();
    }
  }

  ~ScopedLockT() {
    if (Inverted) {
      Lock.lock();
    } else {
      Lock.unlock();
    }
  }

private:
  T &Lock;
};

/// A stack based object that locks the supplied mutex on construction
/// and unlocks it on destruction.
///
/// Precondition: Mutex unlocked by this thread, undefined otherwise.
typedef ScopedLockT<Mutex, false> ScopedLock;
typedef ScopedLockT<StaticMutex, false> StaticScopedLock;

/// A stack based object that unlocks the supplied mutex on construction
/// and relocks it on destruction.
///
/// Precondition: Mutex locked by this thread, undefined otherwise.
typedef ScopedLockT<Mutex, true> ScopedUnlock;
typedef ScopedLockT<StaticMutex, true> StaticScopedUnlock;

/// Compile time adjusted stack based object that locks/unlocks the supplied
/// ReadWriteLock type. Use the provided typedefs instead of this directly.
template <typename T, bool Read, bool Inverted> class ScopedRWLockT {

  ScopedRWLockT() = delete;
  ScopedRWLockT(const ScopedRWLockT &) = delete;
  ScopedRWLockT &operator=(const ScopedRWLockT &) = delete;
  ScopedRWLockT(ScopedRWLockT &&) = delete;
  ScopedRWLockT &operator=(ScopedRWLockT &&) = delete;

public:
  explicit ScopedRWLockT(T &l) : Lock(l) {
    if (Inverted) {
      if (Read) {
        Lock.readUnlock();
      } else {
        Lock.writeUnlock();
      }
    } else {
      if (Read) {
        Lock.readLock();
      } else {
        Lock.writeLock();
      }
    }
  }

  ~ScopedRWLockT() {
    if (Inverted) {
      if (Read) {
        Lock.readLock();
      } else {
        Lock.writeLock();
      }
    } else {
      if (Read) {
        Lock.readUnlock();
      } else {
        Lock.writeUnlock();
      }
    }
  }

private:
  T &Lock;
};

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, true, false> ScopedReadLock;
typedef ScopedRWLockT<StaticReadWriteLock, true, false> StaticScopedReadLock;

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined
/// otherwise.
typedef ScopedRWLockT<ReadWriteLock, true, true> ScopedReadUnlock;
typedef ScopedRWLockT<StaticReadWriteLock, true, true> StaticScopedReadUnlock;

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, false, false> ScopedWriteLock;
typedef ScopedRWLockT<StaticReadWriteLock, false, false> StaticScopedWriteLock;

/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for writing on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, false, true> ScopedWriteUnlock;
typedef ScopedRWLockT<StaticReadWriteLock, false, true> StaticScopedWriteUnlock;

// Enforce literal requirements for static variants.
//static_assert(std::is_literal_type<StaticMutex>::value,
//              "StaticMutex must be literal type");
//static_assert(std::is_literal_type<StaticConditionVariable>::value,
//              "StaticConditionVariable must be literal type");
//static_assert(std::is_literal_type<StaticReadWriteLock>::value,
//              "StaticReadWriteLock must be literal type");
//static_assert(std::is_literal_type<StaticUnsafeMutex>::value,
//              "StaticUnsafeMutex must be literal type");
}

#endif
